package com.atguigu.gulimall.product.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.atguigu.common.utils.PageUtils;
import com.atguigu.common.utils.Query;
import com.atguigu.gulimall.product.dao.CategoryDao;
import com.atguigu.gulimall.product.entity.CategoryEntity;
import com.atguigu.gulimall.product.service.CategoryBrandRelationService;
import com.atguigu.gulimall.product.service.CategoryService;
import com.atguigu.gulimall.product.vo.Catalog2Vo;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.annotation.Caching;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;


@Service("categoryService")
public class CategoryServiceImpl extends ServiceImpl<CategoryDao, CategoryEntity> implements CategoryService {
    @Autowired
    private CategoryBrandRelationService categoryBrandRelationService;
    @Autowired
    private StringRedisTemplate redisTemplate;
    @Autowired
    private RedissonClient redissonClient;

    @Override
    public PageUtils queryPage(Map<String, Object> params) {
        IPage<CategoryEntity> page = this.page(
                new Query<CategoryEntity>().getPage(params),
                new QueryWrapper<CategoryEntity>()
        );

        return new PageUtils(page);
    }

    @Override
    public List<CategoryEntity> listWithTree() {
        //获取所有的分类列表
        List<CategoryEntity> categoryEntities = baseMapper.selectList(null);
        //对所有分类列表进行过滤,得到所有1级分类
        List<CategoryEntity> collect = categoryEntities.stream().filter(categoryEntity ->
                categoryEntity.getParentCid() == 0
        ).map(categoryEntity -> {
            //给所有1级分类下子分类进行赋值
            categoryEntity.setChildren(getChildren(categoryEntity, categoryEntities));
            return categoryEntity;
        }).sorted((menu1, menu2) -> {
            //对所有的分类进行排序
            return (menu1.getSort() == null ? 0 : menu1.getSort()) - (menu2.getSort() == null ? 0 : menu2.getSort());
        }).collect(
                //对Stream流转换成List
                Collectors.toList());
        return collect;
    }

    @Override
    public void removeMenuByIds(List<Long> asList) {
        //TODO 根据不同的业务删除
        baseMapper.deleteBatchIds(asList);
    }

    @Override
    public Long[] findCateLogPath(Long catelogId) {
        List<Long> list = new ArrayList<>();

        List<Long> parentPath = findParentPath(catelogId, list);
        //因为那到的是反的所以进行倒叙
        Collections.reverse(parentPath);
        return parentPath.toArray(new Long[parentPath.size() - 1]);
    }

    @Caching(evict = {
            @CacheEvict(value = "catalog", key = "'getLevel1Categorys'"),
            @CacheEvict(value = "catalog", key = "'getCatalogJson'")
    })
    @Transactional
    @Override
    public void updateDetail(CategoryEntity category) {
        this.updateById(category);
        categoryBrandRelationService.updateCategory(category.getCatId(), category.getName());
        //TODO 根据条件更新中间表
    }

    @Cacheable(value = "catalog", key = "#root.methodName", sync = true)
    @Override
    public List<CategoryEntity> getLevel1Categorys() {
        return this.list(new QueryWrapper<CategoryEntity>().eq("parent_cid", 0));
    }

    @Cacheable(value = "catalog", key = "#root.methodName", sync = true)
    @Override
    public Map<String, List<Catalog2Vo>> getCatalogJson() {
        return getCatalogJsonFromDB();
    }


    public Map<String, List<Catalog2Vo>> getCatalogJsonToRedisson() {
        String catalogJson = redisTemplate.opsForValue().get("catalogJson");
        if (StringUtils.isEmpty(catalogJson)) {
            return getCatalogJsonFromDB();
        }
        return JSON.parseObject(catalogJson, new TypeReference<Map<String, List<Catalog2Vo>>>() {
        });
    }

    private Map<String, List<Catalog2Vo>> getCatalogJsonFromDbWithRedissonLock() {
        RLock lock = redissonClient.getLock("catalog-json");
        Map<String, List<Catalog2Vo>> catalogJsonFromDB = null;
        try {
            lock.lock();
            catalogJsonFromDB = getCatalogJsonFromDB();
        } catch (Exception e) {
            log.error("Redssion分布式锁查询CatalogJsonFromDB失败：{}", e);
        } finally {
            lock.unlock();
        }
        return catalogJsonFromDB;
    }

    private Map<String, List<Catalog2Vo>> getCatalogJsonFromDB() {
        String catalogJson = redisTemplate.opsForValue().get("catalogJson");
        if (!StringUtils.isEmpty(catalogJson)) {
            return JSON.parseObject(catalogJson, new TypeReference<Map<String, List<Catalog2Vo>>>() {
            });
        }
        List<CategoryEntity> categoryEntities = baseMapper.selectList(null);
        List<CategoryEntity> level1Categorys = this.getLevel1Categorys();
        Map<String, List<Catalog2Vo>> collect = level1Categorys.stream().collect(Collectors.toMap(item -> item.getCatId().toString(), level1Category -> {
            List<CategoryEntity> level2Categorys = this.getCategoryEntitysByParentCid(level1Category.getCatId(), categoryEntities);
            List<Catalog2Vo> catalog2Vos = null;
            if (level2Categorys != null && level2Categorys.size() > 0) {
                catalog2Vos = level2Categorys.stream().map(level2Category -> {
                    List<CategoryEntity> level3Categorys = this.getCategoryEntitysByParentCid(level2Category.getCatId(), categoryEntities);
                    List<Catalog2Vo.Category3Vo> category3Vos = null;
                    if (level3Categorys != null && level3Categorys.size() > 0) {
                        category3Vos = level3Categorys.stream().map(level3Category -> {
                            Catalog2Vo.Category3Vo category3Vo = new Catalog2Vo.Category3Vo(level2Category.getCatId().toString(), level3Category.getCatId().toString(), level3Category.getName());
                            return category3Vo;
                        }).collect(Collectors.toList());
                    }
                    Catalog2Vo catalog2Vo = new Catalog2Vo(level2Category.getCatId().toString(), category3Vos, level2Category.getCatId().toString(), level2Category.getName());
                    return catalog2Vo;
                }).collect(Collectors.toList());
            }
            return catalog2Vos;
        }));
        redisTemplate.opsForValue().set("catalogJson", JSON.toJSONString(collect), 1, TimeUnit.DAYS);
        return collect;
    }

    private List<CategoryEntity> getCategoryEntitysByParentCid(Long parentCid, List<CategoryEntity> categoryEntities) {
        List<CategoryEntity> collect = categoryEntities.stream().filter(categoryEntity ->
                parentCid.equals(categoryEntity.getParentCid())
        ).collect(Collectors.toList());
        return collect;
    }

    /**
     * 递归收集所有父分类, 这里拿id的顺序是从下网上拿的, 孙子->父亲->爷爷
     */

    private List<Long> findParentPath(Long catelogId, List<Long> list) {
        //拿到的第一个CategoryId,
        list.add(catelogId);
        CategoryEntity byId = this.getById(catelogId);
        //判断该分类是不是顶级父分类
        if (byId.getParentCid() != 0) {
            //不是顶级父分类进行递归获取
            findParentPath(byId.getParentCid(), list);
        }
        return list;
    }

    private List<CategoryEntity> getChildren(CategoryEntity root, List<CategoryEntity> all) {
        List<CategoryEntity> children = all.stream().filter(categoryEntity ->
                //通过过滤获取该分类下的所有下一级分类
                categoryEntity.getParentCid() == root.getCatId()
        ).map(categoryEntity -> {
            //对上面过滤的所有分类的子分类进行递归父子,完成所有的子分类赋值
            categoryEntity.setChildren(getChildren(categoryEntity, all));
            return categoryEntity;
        }).sorted((menu1, menu2) -> {
            //对所有的分类进行排序
            return (menu1.getSort() == null ? 0 : menu1.getSort()) - (menu2.getSort() == null ? 0 : menu2.getSort());
        }).collect(
                //对Stream流转换成List
                Collectors.toList());
        return children;
    }


}